#include <iostream>
#include "Timer.h"
#include "EcoSystem.h"
#include "Environment.h"
#include "FoodWeb.h"
#include "Species.h"
#include "TypicalEnvironment.h"
#include "Chart.h"
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <iomanip>
using namespace std;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR     gdiplusToken;

EcoSystem *ecosystem = NULL;
FoodWeb *fw = NULL;
Environment *env = NULL;

class print_data_timer_task : public TimerTask
{
public:
    void task()
    {
        if (ecosystem != NULL)
            ecosystem->print_data();
    }
};


typedef void(*CommandFunction)();
map<string, CommandFunction> command_map;

typedef void(*SpawnEntityFunction)(int);
map<string, SpawnEntityFunction> spawn_map;

void spawn_grass(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Grass(ecosystem));
}

void spawn_tree(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Tree(ecosystem));
}

void spawn_ant(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Ant(ecosystem));
}

void spawn_cow(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Cow(ecosystem));
}

void spawn_tiger(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Tiger(ecosystem));
}

void spawn_elephant(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Elephant(ecosystem));
}

void spawn_giraffe(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Giraffe(ecosystem));
}

void spawn_bird(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Bird(ecosystem));
}

void spawn_frog(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Frog(ecosystem));
}

void spawn_eagle(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Eagle(ecosystem));
}

void spawn_insect(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Insect(ecosystem));
}

void spawn_snake(int count)
{
    for (int i = 0; i < count; i++)
        ecosystem->spawn_entity(new Snake(ecosystem));
}


void init_foodweb()
{
    fw = new FoodWeb(ecosystem);
    fw->add(TIGER_SPECIES_ID, COW_SPECIES_ID);
    fw->add(TIGER_SPECIES_ID, GIRAFFE_SPECIES_ID);
    fw->add(COW_SPECIES_ID, GRASS_SPECIES_ID);
    fw->add(ANT_SPECIES_ID, GRASS_SPECIES_ID);
    fw->add(ELEPHANT_SPECIES_ID, GRASS_SPECIES_ID);
    fw->add(GIRAFFE_SPECIES_ID, TREE_SPECIES_ID);
    fw->add(INSECT_SPECIES_ID, GRASS_SPECIES_ID);
    fw->add(FROG_SPECIES_ID, INSECT_SPECIES_ID);
    fw->add(SNAKE_SPECIES_ID, FROG_SPECIES_ID);
    fw->add(EAGLE_SPECIES_ID,SNAKE_SPECIES_ID);
    fw->add(EAGLE_SPECIES_ID, BIRD_SPECIES_ID);
    fw->add(BIRD_SPECIES_ID, INSECT_SPECIES_ID);
}

void init_ecosystem()
{
    ecosystem = new EcoSystem;
    ecosystem->set_food_web(fw);
    env = new TypicalEnvironment(ecosystem);
    env->set_default_producer(new Grass(ecosystem));
    ecosystem->set_environment(env);
}




void spawn()
{
    string species;
    int count;
    cin >> species >> count;
    while (cin.fail())
    {
        cin.clear();
        cin.ignore(10000, '\n');

        cout << "spawn <species name> <count>" << endl;

        return;
    }

    if (ecosystem != NULL)
    {
        if (spawn_map.find(species) != spawn_map.end())
        {
            spawn_map[species](count);
            cout << "Spawned [" << species << "]*" << count << endl;
        }
        else
        {
            cout << "No such species \"" << species << "\"." << endl;
        }
    }


}

void run()
{
    if (ecosystem == NULL)
        init_ecosystem();
    ecosystem->run();
}

void resume()
{
    if (ecosystem != NULL)
        ecosystem->resume();
}

void pause()
{
    if (ecosystem != NULL)
        ecosystem->pause();
}

void stop()
{
    if (ecosystem != NULL)
    {
        delete ecosystem;
        ecosystem = NULL;
        env = NULL;

        init_ecosystem();
    }
}

void plot()
{
    string what;
    cin >> what;
    cin.clear();
    cin.ignore(10000, '\n');

    if (ecosystem != NULL)
    {
        if (what == "pie")
            ecosystem->plot_pie();
        else if (what == "temperature")
            ecosystem->plot_temperature();
        else if (what == "relative_humidity")
            ecosystem->plot_relative_humidity();
        else if (what == "brightness")
            ecosystem->plot_brightness();
        else if (what == "rainfall")
            ecosystem->plot_rainfall();
        else
        {
            bool flag = false;
            for (int i = 0; i < MAX_SPECIES_COUNT; i++)
                if (what == SPECIES_NAME[i])
                {
                    ecosystem->plot_species_linechart(i);
                    flag = true;
                }

            if (!flag)
                cout << "Failed" << endl;
        }
    }
}
void data()
{
    if (ecosystem != NULL)
        ecosystem->print_data();
}

void flush_memory()
{
    if (ecosystem != NULL)
        ecosystem->flush_memory();
}

void enable_image()
{
    if (ecosystem != NULL)
        ecosystem->enable_image();
}

void disable_image()
{
    if (ecosystem != NULL)
        ecosystem->disable_image();
}

void set_time()
{
    unsigned int time;
    cin >> time;
    while (cin.fail())
    {
        cin.clear();
        cin.ignore(10000, '\n');

        cout << "settime <time>" << endl;

        return;
    }

    TypicalEnvironment *tmp = dynamic_cast<TypicalEnvironment*>(env);
    if (tmp != NULL)
        tmp->set_current_tick(time);
}


void init_spawn_map()
{
    spawn_map.insert(pair<string, SpawnEntityFunction>("Grass", spawn_grass));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Cow", spawn_cow));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Tiger", spawn_tiger));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Ant", spawn_ant));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Giraffe", spawn_giraffe));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Elephant", spawn_elephant));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Tree", spawn_tree));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Bird", spawn_bird));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Frog", spawn_frog));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Eagle", spawn_eagle));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Insect", spawn_insect));
    spawn_map.insert(pair<string, SpawnEntityFunction>("Snake", spawn_snake));
}

void init_command_map()
{
    command_map.insert(pair<string, CommandFunction>("spawn", spawn));
    command_map.insert(pair<string, CommandFunction>("run", run));
    command_map.insert(pair<string, CommandFunction>("resume", resume));
    command_map.insert(pair<string, CommandFunction>("stop", stop));
    command_map.insert(pair<string, CommandFunction>("pause", pause));
    command_map.insert(pair<string, CommandFunction>("data", data));
    command_map.insert(pair<string, CommandFunction>("flushMemory", flush_memory));
    command_map.insert(pair<string, CommandFunction>("disableImage", disable_image));
    command_map.insert(pair<string, CommandFunction>("enableImage", enable_image));
    command_map.insert(pair<string, CommandFunction>("plot", plot));
    command_map.insert(pair<string, CommandFunction>("timeset", set_time));
}
void get_command()
{
    string command;
    while (cin >> command && command != "exit")
    {
        if (command_map.find(command) != command_map.end())
            command_map[command]();
        else
        {
            cout << "Command not found." << endl;
            cin.ignore(100000, '\n');
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        cout << "Please run ControlPanel.exe" << endl;
        string cmd;
        cin >> cmd;
        if (cmd != "continue")
            return 0;
    }
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    srand(time(NULL));
    init_command_map();
    init_spawn_map();
    init_foodweb();
    init_ecosystem();

    Timer print_data_timer(1000, print_data_timer_task());
    print_data_timer.start();
    get_command();
    print_data_timer.stop();
    Gdiplus::GdiplusShutdown(gdiplusToken);
    return 0;
}